package core.game;


import recorder.input.InputManagerRecorder;
import recorder.input.InputRecorder;

import java.applet.Applet;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferStrategy;



@SuppressWarnings("serial")
public class GameApplet extends Applet implements Runnable{

	protected Game game;
	
	private int width;
	private int height;
	private InputRecorder inputRecorder;
	private InputManagerRecorder inputManagerRecorder;
	
	private Canvas canvas;
	private BufferStrategy bufferStrategy;
	
	@Override
	public void start(){
		if(bufferStrategy == null){
			setPreferredSize(new Dimension(width, height));
			this.setSize(new Dimension(width, height));

			canvas = new Canvas();
			canvas.setBounds(0, 0, width, height);
			add(canvas);

			canvas.createBufferStrategy(2);
			bufferStrategy = canvas.getBufferStrategy();

			canvas.addMouseListener(new MouseController());
			canvas.addMouseMotionListener(new MouseController());
			canvas.addKeyListener(new KeyController());
			canvas.addFocusListener(new FocusController());
			canvas.requestFocus();
			
			startGame();
		}
		new Thread(this).start();

	}
	
	public void setDisplayMode(int width, int height){
		this.width = width;
		this.height = height;
		game.width = width;
		game.height = height;
	}
	
	public void startGame(){
		inputRecorder = (InputRecorder) game.input;
		inputManagerRecorder = (InputManagerRecorder) game.inputManager;
		game.init();
	}
	
	/*
	 * Game Loop part
	 */
	
	long desiredFPS = 60;
    long desiredDeltaLoop = (1000*1000*1000)/desiredFPS;
    
    long desiredDeltaUpdate = 10;
	
    long totalUpdateTime = 0;
    long totalRunTime = 0;
    
	public void run(){
		
		long beginRunTime;
		
		long beginLoopTime;
		long endLoopTime;
		long currentUpdateTime = System.nanoTime();
		long lastUpdateTime;
		long updateDelta;
		long deltaLoop;
		
		while(!isActive()){
			Thread.yield();
		}
		
		beginRunTime = System.nanoTime();
		while(true){
			beginLoopTime = System.nanoTime();
			
			render();
			
			lastUpdateTime = currentUpdateTime;
			currentUpdateTime = System.nanoTime();
			updateDelta = (currentUpdateTime - lastUpdateTime)/(1000*1000);
			totalUpdateTime += updateDelta;
			totalRunTime = (System.nanoTime() - beginRunTime)/(1000*1000);
			update((int)(updateDelta));
			
			endLoopTime = System.nanoTime();
			deltaLoop = endLoopTime - beginLoopTime;
	        
	        if(deltaLoop > desiredDeltaLoop){
	            //Do nothing. We are already late
	        }else{
	            try{
	                Thread.sleep((desiredDeltaLoop - deltaLoop)/(1000*1000));
	            }catch(InterruptedException e){
	                //Do nothing
	            }
	        }
	        if(!isActive()){
	        	return;
	        }
		}
	}
	
	private void render(){
		try{
			Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
			g.setColor(Color.white);
			g.fillRect(0, 0, width, height);
			//g.clearRect(0, 0, width, height);
			render(g);
			g.dispose();
			bufferStrategy.show();
		}catch(Exception e){
			//System.out.println("error");
			//e.printStackTrace();
		}
	}
	
	private void update(int deltaTime){
		game.update(deltaTime);
	}
	
	private void render(Graphics2D g){
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
		g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
		game.render(g);
		//Testing of timing
		//g.setColor(java.awt.Color.white);
		//g.drawString(totalUpdateTime + " : " + totalRunTime, 600, 50);
		//g.drawString("" + (totalUpdateTime - totalRunTime), 600, 70);
	}
	
	/*
	 * Mouse and key control
	 */
	
	private class MouseController implements MouseListener, MouseMotionListener{

		//@Override
		public void mouseClicked(MouseEvent e) {
			inputManagerRecorder.mouseClicked(e);
		}

		//@Override
		public void mouseEntered(MouseEvent e) {
			
		}

		//@Override
		public void mouseExited(MouseEvent e) {
			
		}

		//@Override
		public void mousePressed(MouseEvent e) {
			inputRecorder.setMouseDown(e.getButton(), true);
			inputManagerRecorder.mousePressed(e);
		}

		//@Override
		public void mouseReleased(MouseEvent e) {
			inputRecorder.setMouseDown(e.getButton(), false);
			inputManagerRecorder.mouseReleased(e);
		}

		//@Override
		public void mouseDragged(MouseEvent e) {
			inputRecorder.setMouseX(e.getX());
			inputRecorder.setMouseY(e.getY());
			inputManagerRecorder.mouseMoved(e);
		}

		//@Override
		public void mouseMoved(MouseEvent e) {
			inputRecorder.setMouseX(e.getX());
			inputRecorder.setMouseY(e.getY());
			inputManagerRecorder.mouseMoved(e);
		}
		
	}
	
	private class KeyController implements KeyListener{

		@Override
		public void keyPressed(KeyEvent e) {
			inputRecorder.setKeyPressed(e.getKeyCode(), true);
			inputManagerRecorder.keyPressed(e);
		}

		@Override
		public void keyReleased(KeyEvent e) {
			inputRecorder.setKeyPressed(e.getKeyCode(), false);
			inputManagerRecorder.keyReleased(e);
		}

		@Override
		public void keyTyped(KeyEvent e) {
			inputManagerRecorder.keyTyped(e);
		}	
	}
	
	private class FocusController implements FocusListener{

		//@Override
		public void focusGained(FocusEvent e) {
			//Do nothing
		}

		//@Override
		public void focusLost(FocusEvent e) {
			inputRecorder.reset();
		}
		
	}
	
}
